import { h, difference } from "koishi"

export const name = "help-extension"

export function apply(ctx) {
  function formatList(session, list, separator, cb) {
    const res = [...cb(list[0])]
    for (let i = 1; i < list.length; i++)
      res.push(...session.i18n(separator), ...cb(list[i]))
    return res
  }

  function formatPermissions(session, permissions) {
    return formatList(session, permissions, "general.or", p => {
      const f = session.i18n([`permissions.${p.replaceAll(":", ".")}`, ""])
      if (!f.length) return [p]
      return f
    })
  }

  function namedOptionForm(name) {
    return (name.length > 1 ? "--" : "-") + name
  }

  function stringifyAlias(alias, command) {
    let str = command.displayName
    for (let [optionName, optionValue] of Object.entries(alias.options)) {
      const option = command._options[optionName]
      if (optionValue === option?.fallback) continue

      let optionForm
      {
        let optionEntry
        if (
          optionEntry = Object.entries(command._symbolicOptions)
            .find(([name, option]) => option.name === optionName && option.values?.[name] === optionValue)
        ) {
          optionForm = optionEntry[0]
          optionValue = true
        } else if (
          optionEntry = Object.entries(command._namedOptions)
            .find(([name, option]) => option.name === optionName && option.values?.[name] === optionValue)
        ) {
          optionForm = namedOptionForm(optionEntry[0])
          optionValue = true
        } else if (
          optionEntry = Object.entries(command._symbolicOptions)
            .find(([, option]) => option.name === optionName)
        ) {
          optionForm = optionEntry[0]
        } else if (
          optionEntry = Object.entries(command._namedOptions)
            .find(([, option]) => option.name === optionName)
        ) {
          let name = optionEntry[0]
          if (optionValue === false) {
            name = `no-${k}`
            optionValue = true
          }
          optionForm = namedOptionForm(name)
        } else {
          optionForm = namedOptionForm(optionName)
        }
      }

      str += " " + optionForm
      if (optionValue !== true) {
        str += " "
        str += (
          /\s|^'.*'$|^".*"$|^‘.*’$|^“.*”$/s.test(optionValue) &&
            !(option?.type && ctx.$commander.resolveDomain(option.type)?.greedy)
          ? `"${optionValue}"`
          : optionValue
        )
      }
    }

    for (const [argIndex, argValue] of Object.entries(alias.args)) {
      str += " "
      str += (
        /^-|\s|^'.*'$|^".*"$|^‘.*’$|^“.*”$/.test(argValue) &&
          !(command._arguments[argIndex] && ctx.$commander.resolveDomain(command._arguments[argIndex].type)?.greedy)
        ? `"${argValue}"`
        : argValue
      )
    }
    return str
  }

  function formatAliases(session, aliases, command) {
    return formatList(session, aliases, "general.comma", ([k, v]) => {
      try {
        if (v?.args?.length || v?.options && Object.keys(v.options).length) return session.i18n(".lnnbot-alias-source", [k, stringifyAlias(v, command)])
      } catch (err) {
        ctx.logger.warn(err)
      }
      return [k]
    })
  }

  ctx.on("help/command", (output, command, session) => {
    revampAliases: if (Object.keys(command._aliases).length > 1) {
      const i = output.lastIndexOf(session.text('.command-aliases', [Object.keys(command._aliases).slice(1).join('，')]))
      if (i === -1) break revampAliases
      const aliases = Object.entries(command._aliases).slice(1).filter(a => session.resolve(a[1]?.filter ?? true))
      if (aliases.length) output[i] = session.text(".command-aliases", [formatAliases(session, aliases, command)])
      else output.splice(i, 1)
    }

    const permissions = difference(session.resolve(command.config?.permissions) ?? [], ["authority:0", "authority:1"])
    if (!permissions.length) return
    output.push(session.text(".lnnbot-required-permissions", [formatPermissions(session, permissions)]))
  })

  ctx.on("help/option", (line, option, command, session) => {
    const permissions = difference(session.resolve(option.permissions) ?? [], ["authority:0", "authority:1"])
    if (!permissions.length) return line
    line += " " + session.text("general.paren", [session.i18n(".lnnbot-required-permissions", [formatPermissions(session, permissions)])])
    return line
  })

  ctx.i18n.define("zh-CN", {
    commands: {
      help: {
        messages: {
          "lnnbot-required-permissions": "需要权限：{0}",
          "lnnbot-alias-source": "{0}（= {1}）",
        },
      },
    },
    permissions: {
      authority: {
        "2": "高级用户——权限等级 2 ",
        "3": "管理员——权限等级 3 ",
        "4": "高级管理员——权限等级 4 ",
        "5": "超级管理员——权限等级 5 ",
      },
    },
  })
  ctx.i18n.define("en-US", {
    commands: {
      help: {
        messages: {
          "lnnbot-required-permissions": "Required permissions: {0}",
        },
      },
    },
    permissions: {
      authority: {
        "2": "advanced user — authority lvl. 2",
        "3": "operator — authority lvl. 3",
        "4": "administrator — authority lvl. 4",
        "5": "super administrator — authority lvl. 5",
      },
    },
  })
}
